#!/usr/bin/env ruby -Ku
require 'rubygems'
require 'mechanize'
require 'cgi'
require "logger"
require "open-uri"
require "kconv"

class NicoAgent
  attr_accessor :logger
  SEARCH_SORT_ORDER = {
    :POSTED_NEW =>{:sort=>:f}, 
    :POSTED_OLD => {:sort=>:f,:order=>:a},
    :VIEWED_MORE => {:sort=>:v},
    :VIEWED_LESS => {:sort=>:v,:order=>:a},
    :COMMENTED_NEW => {:sort=>:n},
    :COMMENTED_OLD => {:sort=>:n, :order=>:a},
    :COMMENTED_MORE => {:sort=>:r},
    :COMMENTED_LESS => {:sort=>:r,:order=>:a}
  }
  ACCESS_INTERVAL = 10.0
  def initialize(mail, pass)
    @agent = WWW::Mechanize.new
    @mail = mail
    @pass = pass
    @logged_in = false
    @last_time = Time.now
  end
  
  def search_by_keyword(keyword, sort)
    url = "http://www.nicovideo.jp/search/"
    ret = search_and_scrape(url, keyword, sort)
  end
  
  def search_by_tag(tag, sort)
    url = "http://www.nicovideo.jp/tag/"
    ret = search_and_scrape(url, tag, sort)
  end
  
  
  def get_flv(video_id, dest_dir_name = nil)
    outname = "#{dest_dir_name}#{File::SEPARATOR}#{video_id}.flv"
    logger.debug("get_flv(video_id: #{video_id})") if logger
    login unless @logged_in
    ret ={:video_id => video_id}
    get_with_auth('http://www.nicovideo.jp/watch/' + video_id) do |page|
      doc = Hpricot(page.body)
      head = try_scrape(doc, "div[@id='des_2']")
      a = try_scrape(head, "p[@class='TXT12']/strong").inner_text.scan(/\d+/)
      # date
      ret[:date] = Time.mktime(a[0], a[1], a[2], a[3], a[4], a[5])
      # title 
      ret[:title] = try_scrape(head, "a[@class='video']").inner_text
      # content
      ret[:content] = try_scrape(head, "p[@class='video_des']").inner_text
      logger.debug("#{ret[:title]}") if logger
    end

    #download flv file if dest_dir_name is set.
    unless dest_dir_name.nil?
      content = get_with_auth('http://www.nicovideo.jp/api/getflv?v=' + video_id).body
      logger.debug(content) if logger
      params = content.scan(/([^&]+)=([^&]*)/).inject({}){|h, v| h[v[0]] = v[1]; h}
      if params.has_key?("url") 
        video_url = CGI.unescape(params['url'])
        open(outname, File::WRONLY | File::CREAT | File::TRUNC | File::BINARY) do |video_file|
          video_file.write get_by_agent(video_url).body
        end
      else
        raise "failed to get #{video_id}.flv. try again."
      end
    end
    #return value
    ret
  end

  private 

  def try_scrape(elem, expression)
    child = elem.search(expression)[0]
    if child.nil?
      raise "failed to get #{expression}"
    end
    child
  end

  def get_with_auth(url)
    page = get_by_agent(url)
    unless authenticated?(page)
      login
      page = get_by_agent(url)
      raise "Failed to get #{url}\n#{page.body}" unless authenticated?(page)
    end

    if block_given?
      yield(page) 
    end
    page
  end

  def get_by_agent(url)
    ret = @agent.get(url)
    interval = ACCESS_INTERVAL - (Time.now -  @last_time)
    if interval > 0 
      logger.debug("sleep:#{interval}")
      sleep(interval)
    end
    @last_time = Time.now
    ret
  end

  def login
    logger.debug("login") if logger
    page = @agent.get("http://www.nicovideo.jp/")
    form = page.form('login_bar')
    form["mail"] = @mail
    form["password"] = @pass
    page = @agent.submit(form)
    raise "Login Failed" unless authenticated?(page)
    @logged_in = true 
  end
  
  def authenticated?(page)
    page.header["x-niconico-authflag"] != "0"
  end

  def search_and_scrape(baseurl, keyword, sort)
    sortby = SEARCH_SORT_ORDER[sort].inject([]) do |r, i| r << "#{i[0]}=#{i[1]}"; r; end.join('&')
    url = "#{baseurl}#{CGI.escape(keyword.toutf8)}?#{sortby}"
    ret = nil


    #<div style="width: 336px;" class="thumb_frm">
    #<p class="TXT10"><nobr>再生：<strong class="vinfo_view">102</strong></nobr>　<nobr>コメント：<strong class="vinfo_res">6</strong></nobr>　<nobr>マイリスト：<strong class="vinfo_mylist">0</strong></nobr></p>
    #<table width="336" cellspacing="0" cellpadding="0" border="0" style="margin-top: 4px;">
    #<tbody><tr valign="top">
    #<td>
    #<p><a href="watch/nm5473954"><img class="video_w96" src="http://tn-skr1.smilevideo.jp/smile?i=5473954" alt="ゆかりんで☆ゲッダン☆"/></a></p>
    #<p style="margin-top: 2px;" class="TXT10"><strong class="vinfo_length">0:49</strong></p>
    #</td>
    #<td width="100%" style="padding-left: 4px;">
    #<p style="margin-bottom: 2px;"><img alt="NicoMovieMaker" src="http://res.nicovideo.jp/img/common/cms/nm.gif"/></p>
    #<p class="TXT10"><strong class="vinfo_posted">08/12/07 11:35</strong> 投稿</p>
    #<p class="TXT12"><a class="video" href="watch/nm5473954"><span class="vinfo_title">ゆかりんで☆ゲッダン☆</span></a><br/>
    #<span class="vinfo_description">初めて手描きで動画を作りました。構想では色もあったりもっとコマもありましたが...</span></p>
    #</td>
    #</tr>
    #</tbody></table>
    #<p class="vinfo_last_res">ｗ ゆかりーーー、俺だー 4 3 2 1 </p>
    #</div>
    get_with_auth(url) do |page|
      doc = Hpricot(page.body)
      divs = (doc/"div[@class='thumb_frm']")
      ret = divs.map do |div| 
        {:video_id => try_scrape(div, "a[@class='video']")['href'].split('/')[1], 
         :title => try_scrape(div, "span[@class='vinfo_title']").inner_text, 
         :comment => try_scrape(div, "span[@class='vinfo_description']").inner_text}
      end
    end
    ret
  end

end
